/**
 *
 */
package com.angel.io.processors.runners.exports.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.apache.log4j.Logger;

import com.angel.common.helpers.StringHelper;
import com.angel.io.descriptor.ExportFileProcessorDescriptor;
import com.angel.io.descriptor.ExportedFileDescriptor;
import com.angel.io.exceptions.InvalidRowDataException;
import com.angel.io.factories.FileColumnsFactory;
import com.angel.io.processors.commands.exports.ExportFileProcessorCommand;
import com.angel.io.processors.commands.exports.ExportRowProcessorCommand;
import com.angel.io.processors.runners.exports.ExportProcessorRunner;
import com.angel.io.type.rows.ExportRowFile;
import com.angel.io.type.rows.impl.HeaderRowFile;

/**
 * @author William
 *
 */
public class ExportFileProcessorRunner implements ExportProcessorRunner{

	private static final Logger LOGGER = Logger.getLogger(ExportFileProcessorRunner.class);

	private ExportFileProcessorDescriptor fileProcessorDescriptor;
	protected ExportRowProcessorCommand exportRowCommand;
	protected ExportFileProcessorCommand exportFileCommand;
	private HeaderRowFile headerRow;

	public ExportFileProcessorRunner(ExportFileProcessorDescriptor fileProcessorDescriptor, ExportRowProcessorCommand exportRowCommand, ExportFileProcessorCommand exportFileCommand){
		super();
		this.exportFileCommand = exportFileCommand;
		this.exportRowCommand = exportRowCommand;
		this.fileProcessorDescriptor = fileProcessorDescriptor;
		this.headerRow = new HeaderRowFile();
	}

	/* (non-Javadoc)
	 * @see com.angel.io.processors.runner.interfaces.ProcessorRunner#runProcess(java.io.InputStream)
	 */
	public ExportedFileDescriptor runProcess(Collection<?> objects) throws InvalidRowDataException{
		ExportedFileDescriptor exportedFileDescriptor = new ExportedFileDescriptor(new Date());
		LOGGER.info("Export process is initializing...");
		this.exportFileCommand.initialize();

		LOGGER.info("Creating file header...");
		this.exportFileCommand.createHeader(this.getHeaderRow(),this.getFileProcessorDescriptor().getColumnSeparator());

		LOGGER.info("Preparing file...");
		List<ExportRowFile> exportRows = this.exportFileCommand.prepareFile(this.getHeaderRow(), Integer.valueOf(objects.size()), this.fileProcessorDescriptor.getColumnSeparator());

		this.processEachExportRow(exportRows, objects);

		LOGGER.info("Export process is finishing...");
		this.exportFileCommand.finish(exportedFileDescriptor, this.fileProcessorDescriptor, this.getHeaderRow(), exportRows);
		
		exportedFileDescriptor.setEndProcess(new Date());
		LOGGER.info("Export process is finished in [" + exportedFileDescriptor.calculateDuration() + "] milliseconds.");

		return exportedFileDescriptor;
	}
	
	
	protected InputStream transformOutputStreamToInputStream(OutputStream o) throws IOException, InterruptedException, ExecutionException{
		PipedInputStream pin = new PipedInputStream();  
		PipedOutputStream pout = new PipedOutputStream(pin);  
		
		PrintStream out = new PrintStream(pout);  
		BufferedReader in = new BufferedReader(new InputStreamReader(pin));  


		out.flush();  
			
		System.out.println("Text written: " + in.readLine());
		return null;
	}

	private void processEachExportRow(List<ExportRowFile> exportRows, Collection<?> objects) throws InvalidRowDataException {
		LOGGER.info("Checking header row [" + this.getHeaderRow().prepareHeader(this.fileProcessorDescriptor.getColumnSeparator()) + "].");
		this.exportRowCommand.checkHeader(this.getHeaderRow());
		Object[] arrayObjects = objects.toArray();
		long position = 0;
		for(Object o: arrayObjects){
			try {
				this.exportRowCommand.processRow(exportRows.get((int) position), o, this.getHeaderRow());
				this.getFileProcessorDescriptor().getProcessorLog().addProcessedRowMessageLog(position, "OK");
				LOGGER.info("Object processed OK at position number [" + position + "]...");
			} catch(InvalidRowDataException e){
				LOGGER.error("Invalid row data [" + position + "] processing object [" + o.toString() + "].", e);
				this.getFileProcessorDescriptor().getProcessorLog().addFailRowMessageLog(position, e.getMessage());
			}
			position++;
		}
	}

	public void addAliasFor(String columnName, String aliasName) {
		this.headerRow.addAliasFor(columnName, aliasName);
	}

	public void addAliasFor(String columnName, List<String> aliasNames) {
		this.headerRow.addAllAliasFor(columnName, aliasNames);
	}

	public void addColumn(String columnName, Integer position) {
		this.headerRow.addColumn(position, columnName);
	}

	public void addColumns(FileColumnsFactory fileColumnsFactory) {
		fileColumnsFactory.initialize();
		while(fileColumnsFactory.hasMoreColumns()){
			Integer position = fileColumnsFactory.getCurrentPosition();
			String columnName = fileColumnsFactory.getNextColumnName();
			LOGGER.info("Adding column to header [" + columnName + "] at position [" + position + "].");
			this.addOrReplaceColumn(columnName, position);
		}
	}

	private void addOrReplaceColumn(String columnName, int position) {
		if(StringHelper.isNotEmpty(this.headerRow.getColumnName(position))){
			this.headerRow.replaceColumn(position, columnName);
		} else {
			this.headerRow.addColumn(position, columnName);
		}
	}

	public void addFirstColumn(String columnName) {
		this.headerRow.addFirstColumn(columnName);
	}

	public void addNextColumn(String columnName) {
		this.headerRow.addNextColumn(columnName);
	}

	/**
	 * @return the fileProcessorDescriptor
	 */
	public ExportFileProcessorDescriptor getFileProcessorDescriptor() {
		return fileProcessorDescriptor;
	}

	/**
	 * @return the headerRow
	 */
	protected HeaderRowFile getHeaderRow() {
		return headerRow;
	}

}
